using System;

namespace RapidHDL
{
	/// <summary>
	/// Summary description for Node.
	/// </summary>
	public class Node
	{
		NodeStructure eNodeStructure;
		NodeState eNodeState;
		int iPosition;
		NodeVector oParentNodeVector;
		Net oNet;
		
		bool bNodeConnectsParentComponent;
	

		public Node(NodeVector poParentNodeVector, int piPosition, NodeState peNodeState)
		{
			oParentNodeVector = poParentNodeVector;
			iPosition = piPosition;
			eNodeState = peNodeState;
			oNet = new Net(this);
			eNodeStructure = NodeStructure.Unknown;

			bNodeConnectsParentComponent = false;
		}

		public Node(NodeVector poParentNodeVector, int piPosition)
			: this(poParentNodeVector,piPosition,NodeState.Undefined)
		{
		}

		public NodeState NodeState 
		{
			set
			{
				switch(this.NodeFlowType)
				{
					case NodeFlowType.Passthrough :
					case NodeFlowType.Sink :
						oNet.NodeState = value;
						break;
					case NodeFlowType.Source :
						eNodeState = value;
						break;
				}
			}

			get
			{
				switch(this.NodeFlowType)
				{
					case NodeFlowType.Source :
						return eNodeState;
					default :
						return this.Net.NodeState;
				}
			}
		}

		public char NodeStateAsChar
		{
			set
			{
				this.NodeState = NodeState.Undefined;
				switch(value)
				{
					case '1': 
						this.NodeState = NodeState.High;
						break;
					case '0':
						this.NodeState = NodeState.Low;
						break;
					case 'x':
					case 'X':
						this.NodeState = NodeState.Undefined;
						break;
					case 'z':
					case 'Z':
						this.NodeState = NodeState.Tri;
						break;
					default:
						System.Diagnostics.Debug.Fail("Invalid node state character");
						break;
				}
			}

			get
			{
				switch(this.NodeState)
				{
					case NodeState.High: return '1';
					case NodeState.Low: return '0';
					case NodeState.Tri: return 'z';
					default: return 'x';
				}
			}
		}

		public NodeFlowType NodeFlowType 
		{
			get{return oParentNodeVector.NodeFlowType;}
		}

		public NodeStructure NodeStructure
		{
			get{return eNodeStructure;}
			set{eNodeStructure = value;}
		}

		public int Position
		{
			get {return iPosition;}
		}

		public NodeVector ParentNodeVector
		{
			get { return oParentNodeVector;}
		}

		public string Name
		{
			get{ return oParentNodeVector.Name + "[" + iPosition.ToString() + "]";}
		}

		public Net Net
		{
			set {oNet = value;}
			get {return oNet;}
		}
		
		public override string ToString()
		{
			return NodeStateAsChar.ToString();
		}

		public string HirarchicalName
		{
			get{return oParentNodeVector.HirarchicalName +  "[" + iPosition.ToString() + "]";}
		}

		public bool NodeConnectsParentComponent
		{
			// return true if this node is connected
			//		to another node that belongs to the parent component
			get
			{
				bool bNodeConnectsParentComponent = false;

				return bNodeConnectsParentComponent;
			}
		}

		/*
		public System.Collections.ArrayList IdenticalNodes
		{
			get {return oIdenticalNodes;}
		}


		public void ParseNetStructure()
		{
			// are identical nodes used for anything?

			oIdenticalNodes = new System.Collections.ArrayList();
			int iParentHirarchyLevel = this.ParentNodeVector.ParentComponent.HirarchyLevel - 1;

			bNodeConnectsParentComponent = false;

			foreach(Node oNode in oNet.SourceNodes)
			{
				if (oNode.ParentNodeVector.ParentComponent.HirarchyLevel == iParentHirarchyLevel)
				{
					if (oNode.ParentNodeVector.ParentComponent == this.ParentNodeVector.ParentComponent.ParentComponent)
						bNodeConnectsParentComponent = true;
				}
				if (oNode.ParentNodeVector.ParentComponent == this.ParentNodeVector.ParentComponent)
				{
					if (!(oNode == this))
					{
						oIdenticalNodes.Add(oNode);
					}
				}

			}


		}*/

		// rename nodestructure to netstructure
		public NodeStructure OrganizeNodeStructure()
		{
			Node oSourceNode;

			// temporary way to get source node
			if(oNet.SourceNodes.Count == 0)
			{
				oSourceNode = null;
			}
			else
			{
				oSourceNode = (Node)oNet.SourceNodes[0];
			}

			// if NodeStructue is already defined for this node
			// then don't waste any more time
			//		could be defined by user
			//		could be defined by node at same component level
			if (eNodeStructure != NodeStructure.Unknown)
			{
				return eNodeStructure;
			}

			// check for a higher level sink/passthrough node
			int iLowestSinkHirarchy = oParentNodeVector.ParentComponent.HirarchyLevel;


			// check to see if any nodes have been user defined
			// that are at this same component level
			//		at the same time
			// mark any undefined nodes at this component level
			// to be redundant because a critical node will be defined
			// for this component level, before this algorithm is over
			foreach(Node oNode in oNet.PassthroughNodes)
			{
				// find nodes at this component level
				if(oNode.ParentNodeVector.ParentComponent == oParentNodeVector.ParentComponent)
				{
					// mark them redundant if the node is not this node
					if(oNode != this)
					{
						// but, if the other node is already defined, we become redundant
						if (oNode.NodeStructure == NodeStructure.Unknown | oNode.NodeStructure == NodeStructure.Redundant)
							oNode.NodeStructure = NodeStructure.Redundant;
						else
							eNodeStructure = NodeStructure.Redundant;
					}
				}
				// keeping track of the highest non-source hirarchy might save us some work later
				// when we want to know if there is a non-child sink/passthrough node
				if(oNode.ParentNodeVector.ParentComponent.HirarchyLevel < iLowestSinkHirarchy)
					iLowestSinkHirarchy = oNode.ParentNodeVector.ParentComponent.HirarchyLevel;
			}
			foreach(Node oNode in oNet.SinkNodes)
			{
				// find nodes at this component level
				if(oNode.ParentNodeVector.ParentComponent == oParentNodeVector.ParentComponent & oNode != this)
				{
					// mark them redundant if the node is not this node
					if(oNode != this)
					{
						// but, if the other node is already defined, we become redundant
						if (oNode.NodeStructure == NodeStructure.Unknown | oNode.NodeStructure == NodeStructure.Redundant)
							oNode.NodeStructure = NodeStructure.Redundant;
						else
							eNodeStructure = NodeStructure.Redundant;
					}
				}
				// keeping track of the highest non-source hirarchy might save us some work later
				// when we want to know if there is a non-child sink/passthrough node
				if(oNode.ParentNodeVector.ParentComponent.HirarchyLevel < iLowestSinkHirarchy)
					iLowestSinkHirarchy = oNode.ParentNodeVector.ParentComponent.HirarchyLevel;
			}
			if (oSourceNode != null)
			{
				if(oSourceNode.ParentNodeVector.ParentComponent == oParentNodeVector.ParentComponent)
				{
					// mark them redundant if the node is not this node
					if(oSourceNode != this)
					{
						// but, if the other node is already defined, we become redundant
						if (oSourceNode.NodeStructure == NodeStructure.Unknown | oSourceNode.NodeStructure == NodeStructure.Redundant)
							oSourceNode.NodeStructure = NodeStructure.Redundant;
						else
							eNodeStructure = NodeStructure.Redundant;
					}
				}
			}
			
			// if we found a node above 
			// that was predefined at our component level in the same net
			// then return this node as redundant
			if(eNodeStructure == NodeStructure.Redundant)
				return eNodeStructure;

			// now, chances are good that this node will become an input, so
			// lets just set it to be an input and adjust later if need be
			eNodeStructure = NodeStructure.Input;


			// a missing source node implies that we *must* be an input
			if(oSourceNode == null)
				return eNodeStructure;

			// any source that is not a child of this component
			//  and that is not in this component
			//  must be an input
			if(!oParentNodeVector.ParentComponent.IsTestComponentSameOrChild(oSourceNode.ParentNodeVector.ParentComponent))
				return eNodeStructure;

			// now, chances are good that this node will become an output, so
			// lets just set it to be an output and adjust later if need be
			eNodeStructure = NodeStructure.Output;

			// if there is a sink or node with a lower hirarchy number than us (or same level)
			// then we know it must be an output 
			// because we must make the connecton through the parent
			if (iLowestSinkHirarchy < oParentNodeVector.ParentComponent.HirarchyLevel)
				return eNodeStructure;

			//but, higher number sink or nodes must be checked one-by-one
			// and if any is not a child of this, then they must be outputs
			// but if all are children of this, then this is a connection only
			foreach(Node oNode in oNet.SinkNodes)
			{
				// check for any sink or passthrough node
				// that is not in a component that is a child or the same as this node's component
				// and if so, this node must be an output to get to that spot
				if(!oParentNodeVector.ParentComponent.IsTestComponentSameOrChild(oNode.ParentNodeVector.ParentComponent))
					return eNodeStructure;
				// note, could be faster if we filtered to check a component only once...
			}
			foreach(Node oNode in oNet.PassthroughNodes)
			{
				// check for any sink or passthrough node
				// that is not in a component that is a child or the same as this node's component
				// and if so, this node must be an output to get to that spot
				if(!oParentNodeVector.ParentComponent.IsTestComponentSameOrChild(oNode.ParentNodeVector.ParentComponent))
					return eNodeStructure;
				// note, could be faster if we filtered to check a component only once...
			}

			// if we haven't returned yet, the node can only be one thing
			// a connection node
			eNodeStructure = NodeStructure.Connection;
			return eNodeStructure;
		}

		public string GetLogIdString(LogType peLogType)
		{
			string sDebug = "{Node: " + HirarchicalName + " [";
			if (peLogType == LogType.VerilogTrace)
			{
				sDebug += "NodeFlowType=" + oParentNodeVector.NodeFlowType.ToString();
				sDebug += ",NodeStructure=" + eNodeStructure.ToString();
			}
			sDebug +="]}";
			return sDebug;
		}
	}
}
